package com.xingyang.yutnori.model;

import java.awt.Point;
import java.util.HashMap;
import java.util.Map;

/**
 * Represents the game board in the Yut game.
 * Defines the positions and paths for moving pieces.
 */
public class Board {
    // Board positions
    private Map<Integer, Point> positions;
    
    // Path type enum
    public enum PathType {
        MAIN_PATH,       // Main circular path
        DIAGONAL_LEFT,   // Left diagonal path
        DIAGONAL_RIGHT,  // Right diagonal path
        CORNER_PATH     // Corner shortcut path
    }
    
    // Path mappings for different path types
    private Map<PathType, Map<Integer, Integer>> pathMaps;
    
    // Special path mappings for shortcuts
    private Map<Integer, Integer> specialPaths;
    
    // Normal direct paths for standard movement
    private Map<Integer, Integer> normalPaths;
    
    // Reverse direction mappings for BackDo movement
    private Map<Integer, Integer> reverseDirections;
    
    // Add class field for tracking diagonal path
    private boolean cameFromPosition5Diagonal = false;
    
    /**
     * Constructs and initializes the game board.
     * Sets up the positions and special paths.
     */
    public Board() {
        initializePositions();
        initializePaths();
        initializeSpecialPaths();
        initializeNormalPaths();
        initializeReverseDirections();
    }
    
    /**
     * Initializes the positions on the board.
     * Each position is mapped to a coordinate for display.
     */
    private void initializePositions() {
        positions = new HashMap<>();
        
        // Board size and margins
        int boardSize = 800;
        int margin = 100;
        int gameArea = boardSize - (2 * margin);
        int step = gameArea / 5;  // Back to 5 steps to maintain original size
        
        // Base coordinates
        int baseX = margin;
        int baseY = margin;
        
        // Top row - right to left (5->10)
        positions.put(5, new Point(baseX + 5*step, baseY));          // Top right
        positions.put(6, new Point(baseX + 4*step, baseY));
        positions.put(7, new Point(baseX + 3*step, baseY));
        positions.put(8, new Point(baseX + 2*step, baseY));
        positions.put(9, new Point(baseX + step, baseY));
        positions.put(10, new Point(baseX, baseY));                  // Top left
        
        // Right side - top to bottom (5->20)
        positions.put(4, new Point(baseX + 5*step, baseY + step));
        positions.put(3, new Point(baseX + 5*step, baseY + 2*step));
        positions.put(2, new Point(baseX + 5*step, baseY + 3*step));
        positions.put(1, new Point(baseX + 5*step, baseY + 4*step));
        positions.put(20, new Point(baseX + 5*step, baseY + 5*step)); // Bottom right
        
        // Left side - top to bottom (10->15)
        positions.put(11, new Point(baseX, baseY + step));
        positions.put(12, new Point(baseX, baseY + 2*step));
        positions.put(13, new Point(baseX, baseY + 3*step));
        positions.put(14, new Point(baseX, baseY + 4*step));
        positions.put(15, new Point(baseX, baseY + 5*step));        // Bottom left
        
        // Bottom row - left to right (15->20)
        positions.put(16, new Point(baseX + step, baseY + 5*step));
        positions.put(17, new Point(baseX + 2*step, baseY + 5*step));
        positions.put(18, new Point(baseX + 3*step, baseY + 5*step));
        positions.put(19, new Point(baseX + 4*step, baseY + 5*step));
        
        // Center point (23) - Made larger like previous position 24
        positions.put(23, new Point(baseX + (5*step)/2, baseY + (5*step)/2));
        
        // First diagonal path: 10 -> 26 -> 27 -> 23 -> 28 -> 29 -> 20
        positions.put(26, new Point(
            baseX + step,
            baseY + step
        ));
        positions.put(27, new Point(
            baseX + 2*step,
            baseY + 2*step
        ));
        positions.put(28, new Point(
            baseX + 3*step,
            baseY + 3*step
        ));
        positions.put(29, new Point(
            baseX + 4*step,
            baseY + 4*step
        ));
        
        // Second diagonal path: 15 -> 25 -> 24 -> 23 -> 22 -> 21 -> 5
        positions.put(25, new Point(
            baseX + step,
            baseY + 4*step
        ));
        positions.put(24, new Point(
            baseX + 2*step,
            baseY + 3*step
        ));
        positions.put(22, new Point(
            baseX + 3*step,
            baseY + 2*step
        ));
        positions.put(21, new Point(
            baseX + 4*step,
            baseY + step
        ));
        
        // Goal position (30) - slightly offset from position 20
        positions.put(30, new Point(
            baseX + (int)(5.5*step),
            baseY + (int)(5.5*step)
        ));
    }
    
    /**
     * Initializes the paths on the board.
     */
    private void initializePaths() {
        pathMaps = new HashMap<>();
        
        // Initialize path maps for each type
        Map<Integer, Integer> mainPath = new HashMap<>();
        Map<Integer, Integer> diagonalLeft = new HashMap<>();
        Map<Integer, Integer> diagonalRight = new HashMap<>();
        Map<Integer, Integer> cornerPath = new HashMap<>();
        
        // Main path - Starting from 20 (START/END)
        mainPath.put(-1, 20);   // From home to start position
        mainPath.put(20, 1);    // 20 -> 1
        mainPath.put(1, 2);     // 1 -> 2
        mainPath.put(2, 3);     // 2 -> 3
        mainPath.put(3, 4);     // 3 -> 4
        mainPath.put(4, 5);     // 4 -> 5
        mainPath.put(5, 6);     // 5 -> 6
        mainPath.put(6, 7);     // 6 -> 7
        mainPath.put(7, 8);     // 7 -> 8
        mainPath.put(8, 9);     // 8 -> 9
        mainPath.put(9, 10);    // 9 -> 10
        mainPath.put(10, 11);   // 10 -> 11
        mainPath.put(11, 12);   // 11 -> 12
        mainPath.put(12, 13);   // 12 -> 13
        mainPath.put(13, 14);   // 13 -> 14
        mainPath.put(14, 15);   // 14 -> 15
        mainPath.put(15, 16);   // 15 -> 16
        mainPath.put(16, 17);   // 16 -> 17
        mainPath.put(17, 18);   // 17 -> 18
        mainPath.put(18, 19);   // 18 -> 19
        mainPath.put(19, 20);   // 19 -> 20 (complete circuit)
        
        // First diagonal path: 10 -> 26 -> 27 -> 23 -> 28 -> 29 -> 20
        diagonalLeft.put(10, 26);   // 10 -> 26
        diagonalLeft.put(26, 27);   // 26 -> 27
        diagonalLeft.put(27, 23);   // 27 -> 23
        diagonalLeft.put(23, 28);   // 23 -> 28
        diagonalLeft.put(28, 29);   // 28 -> 29
        diagonalLeft.put(29, 20);   // 29 -> 20
        
        // Second diagonal path: 5 -> 21 -> 22 -> 23 and 15 -> 25 -> 24 -> 23
        diagonalRight.put(5, 21);   // 5 -> 21
        diagonalRight.put(21, 22);  // 21 -> 22
        diagonalRight.put(22, 23);  // 22 -> 23
        diagonalRight.put(15, 25);  // 15 -> 25
        diagonalRight.put(25, 24);  // 25 -> 24
        diagonalRight.put(24, 23);  // 24 -> 23
        
        // Store all path maps
        pathMaps.put(PathType.MAIN_PATH, mainPath);
        pathMaps.put(PathType.DIAGONAL_LEFT, diagonalLeft);
        pathMaps.put(PathType.DIAGONAL_RIGHT, diagonalRight);
        pathMaps.put(PathType.CORNER_PATH, cornerPath);
    }
    
    /**
     * Initializes the special paths on the board.
     */
    private void initializeSpecialPaths() {
        specialPaths = new HashMap<>();
        
        // Main circle path (clockwise from 20)
        specialPaths.put(-1, 20);  // From home to start
        specialPaths.put(20, 1);   // 20 -> 1
        specialPaths.put(1, 2);
        specialPaths.put(2, 3);
        specialPaths.put(3, 4);
        specialPaths.put(4, 5);
        specialPaths.put(5, 6);
        specialPaths.put(6, 7);
        specialPaths.put(7, 8);
        specialPaths.put(8, 9);
        specialPaths.put(9, 10);
        specialPaths.put(10, 11);
        specialPaths.put(11, 12);
        specialPaths.put(12, 13);
        specialPaths.put(13, 14);
        specialPaths.put(14, 15);
        specialPaths.put(15, 16);
        specialPaths.put(16, 17);
        specialPaths.put(17, 18);
        specialPaths.put(18, 19);
        specialPaths.put(19, 20);  // Back to start/end
    }
    
    /**
     * Initializes the normal path mappings for standard movement
     */
    private void initializeNormalPaths() {
        normalPaths = new HashMap<>();
        
        // Main circle path (clockwise from 20)
        normalPaths.put(-1, 20);   // From home to start position
        normalPaths.put(20, 1);    // 20 -> 1
        normalPaths.put(1, 2);     // 1 -> 2
        normalPaths.put(2, 3);     // 2 -> 3
        normalPaths.put(3, 4);     // 3 -> 4
        normalPaths.put(4, 5);     // 4 -> 5
        normalPaths.put(5, 6);     // 5 -> 6
        normalPaths.put(6, 7);     // 6 -> 7
        normalPaths.put(7, 8);     // 7 -> 8
        normalPaths.put(8, 9);     // 8 -> 9
        normalPaths.put(9, 10);    // 9 -> 10
        normalPaths.put(10, 11);   // 10 -> 11
        normalPaths.put(11, 12);   // 11 -> 12
        normalPaths.put(12, 13);   // 12 -> 13
        normalPaths.put(13, 14);   // 13 -> 14
        normalPaths.put(14, 15);   // 14 -> 15
        normalPaths.put(15, 16);   // 15 -> 16
        normalPaths.put(16, 17);   // 16 -> 17
        normalPaths.put(17, 18);   // 17 -> 18
        normalPaths.put(18, 19);   // 18 -> 19
        normalPaths.put(19, 20);   // 19 -> 20 (complete circuit)
    }
    
    /**
     * Initializes reverse direction mappings for BackDo movement
     */
    private void initializeReverseDirections() {
        reverseDirections = new HashMap<>();
        
        // Main path reverse directions
        reverseDirections.put(1, 20);   // 1 ← 20
        reverseDirections.put(2, 1);    // 2 ← 1
        reverseDirections.put(3, 2);    // 3 ← 2
        reverseDirections.put(4, 3);    // 4 ← 3
        reverseDirections.put(5, 4);    // 5 ← 4
        reverseDirections.put(6, 5);    // 6 ← 5
        reverseDirections.put(7, 6);    // 7 ← 6
        reverseDirections.put(8, 7);    // 8 ← 7
        reverseDirections.put(9, 8);    // 9 ← 8
        reverseDirections.put(10, 9);   // 10 ← 9
        reverseDirections.put(11, 10);  // 11 ← 10
        reverseDirections.put(12, 11);  // 12 ← 11
        reverseDirections.put(13, 12);  // 13 ← 12
        reverseDirections.put(14, 13);  // 14 ← 13
        reverseDirections.put(15, 14);  // 15 ← 14
        reverseDirections.put(16, 15);  // 16 ← 15
        reverseDirections.put(17, 16);  // 17 ← 16
        reverseDirections.put(18, 17);  // 18 ← 17
        reverseDirections.put(19, 18);  // 19 ← 18
        reverseDirections.put(20, 19);  // 20 ← 19
        
        // First diagonal path reverse directions (10 -> 20)
        reverseDirections.put(26, 10);  // 26 ← 10
        reverseDirections.put(27, 26);  // 27 ← 26
        reverseDirections.put(23, 27);  // 23 ← 27
        reverseDirections.put(28, 23);  // 28 ← 23
        reverseDirections.put(29, 28);  // 29 ← 28
        reverseDirections.put(20, 29);  // 20 ← 29
        
        // Second diagonal path reverse directions (15 -> 5)
        reverseDirections.put(25, 15);  // 25 ← 15
        reverseDirections.put(24, 25);  // 24 ← 25
        reverseDirections.put(23, 24);  // 23 ← 24
        reverseDirections.put(22, 23);  // 22 ← 23
        reverseDirections.put(21, 22);  // 21 ← 22
        reverseDirections.put(5, 21);   // 5 ← 21
    }
    
    /**
     * Gets the path map for a specific path type
     */
    public Map<Integer, Integer> getPathMap(PathType type) {
        return pathMaps.get(type);
    }
    
    /**
     * Gets the next position based on the current position and roll.
     */
    public int getNextPosition(int currentPosition, int roll) {
        System.out.println("Getting next position: from " + currentPosition + " moving " + roll + " steps");
        
        // Invalid roll, maintain current position
        if (roll == 0) {
            return currentPosition;
        }
        
        // Handle BackDo (backward movement) special case
        if (roll == YutSet.BACKDO) {
            // Cannot move backward from home
            if (currentPosition == -1) {
                System.out.println("Cannot move backward from home position");
                return currentPosition;
            }
            
            // Use reverse directions map for backward movement
            Integer backPos = reverseDirections.get(currentPosition);
            if (backPos != null) {
                System.out.println("Backward movement from " + currentPosition + " to " + backPos);
                return backPos;
            }
            return currentPosition;
        }
        
        // For initial movement from home, move directly based on roll value
        if (currentPosition == -1) {
            return roll;  // Move directly to the position matching the roll value
        }
        
        int currentPos = currentPosition;
        int stepsLeft = roll;
        PathType currentPath = PathType.MAIN_PATH;
        
        while (stepsLeft > 0) {
            Integer nextPos = null;
            
            // Special position handling
            if (currentPos == 20) {
                nextPos = 1;
            } else if (currentPos == 29) {
                nextPos = 20;
            } else if (currentPos == 10) {
                // Try diagonal path first, if not chosen use main path
                Map<Integer, Integer> diagonalPath = pathMaps.get(PathType.DIAGONAL_LEFT);
                nextPos = diagonalPath.get(currentPos);
                if (nextPos != null) {
                    currentPath = PathType.DIAGONAL_LEFT;
                } else {
                    Map<Integer, Integer> mainPathMap = pathMaps.get(PathType.MAIN_PATH);
                    nextPos = mainPathMap.get(currentPos);
                }
            } else if (currentPos == 5) {
                // Try diagonal path first, if not chosen use main path
                Map<Integer, Integer> diagonalPath = pathMaps.get(PathType.DIAGONAL_RIGHT);
                nextPos = diagonalPath.get(currentPos);
                if (nextPos != null) {
                    currentPath = PathType.DIAGONAL_RIGHT;
                    cameFromPosition5Diagonal = true;
                } else {
                    Map<Integer, Integer> mainPathMap = pathMaps.get(PathType.MAIN_PATH);
                    nextPos = mainPathMap.get(currentPos);
                }
            } else {
                Map<Integer, Integer> pathMap = pathMaps.get(currentPath);
                nextPos = pathMap.get(currentPos);
                
                if (nextPos == null && currentPath != PathType.MAIN_PATH) {
                    pathMap = pathMaps.get(PathType.MAIN_PATH);
                    nextPos = pathMap.get(currentPos);
                    currentPath = PathType.MAIN_PATH;
                }
            }
            
            if (nextPos == null) {
                System.out.println("No valid next position found from " + currentPos);
                return currentPos;
            }
            
            System.out.println("Moving from " + currentPos + " to " + nextPos + " on path " + currentPath);
            currentPos = nextPos;
            stepsLeft--;
            
            // Handle reaching position 20
            if (currentPos == 20 && stepsLeft > 0) {
                currentPos = 1;
                stepsLeft--;
            }
        }
        
        // If landed exactly on 20 (not from starting position), mark as completed
        if (currentPos == 20 && currentPosition != -1) {
            return 30;
        }
        
        return currentPos;
    }
    
    private int getPreviousPosition(int currentPosition) {
        if (currentPosition == 0) return 19;
        
        // Check if on a special path
        for (Map.Entry<Integer, Integer> entry : specialPaths.entrySet()) {
            if (entry.getValue() == currentPosition) {
                return entry.getKey();
            }
        }
        
        return currentPosition - 1;
    }
    
    /**
     * Gets the coordinate for a position on the board.
     * 
     * @param position The position to get coordinates for
     * @return The coordinates, or null if position is invalid
     */
    public Point getCoordinate(int position) {
        // Special validation for positions 8 and 9 which were potentially problematic
        if (position == 8 || position == 9) {
            System.out.println("Getting coordinates for special position: " + position);
            Point result = positions.get(position);
            if (result != null) {
                System.out.println("  Position " + position + " coordinates: (" + result.x + "," + result.y + ")");
            } else {
                System.out.println("  ERROR: No coordinates found for position " + position);
            }
            return result;
        }
        
        Point result = positions.get(position);
        if (result == null) {
            System.out.println("Warning: No coordinate found for position " + position);
        }
        return result;
    }
    
    public boolean isCorner(int position) {
        return position == 0 || position == 5 || position == 10 || 
               position == 15 || position == 20 || position == 23;
    }
    
    public boolean hasShortcut(int position) {
        return specialPaths.containsKey(position);
    }
    
    public int getShortcutDestination(int position) {
        return specialPaths.getOrDefault(position, -1);
    }
    
    public Map<Integer, Point> getPositions() {
        return positions;
    }
    
    /**
     * Checks if the position is a junction where player needs to choose a path
     */
    public boolean isJunction(int position) {
        return position == 5 || position == 10 || position == 15 || position == 20 || position == 23;
    }

    /**
     * Gets available paths from a junction position
     * @param position The current position
     * @param previousPosition The position we came from (-1 if unknown)
     * @return Map of path type to next position, empty if not a junction
     */
    public Map<PathType, Integer> getAvailablePaths(int position, int previousPosition) {
        Map<PathType, Integer> availablePaths = new HashMap<>();
        
        // Always check main path first
        Map<Integer, Integer> mainPath = pathMaps.get(PathType.MAIN_PATH);
        if (mainPath.containsKey(position)) {
            availablePaths.put(PathType.MAIN_PATH, mainPath.get(position));
        }
        
        // Check for diagonal paths at junctions
        if (position == 5) {
            // At position 5 - can go main path (5->6) or right diagonal (5->21)
            Map<Integer, Integer> rightDiagonal = pathMaps.get(PathType.DIAGONAL_RIGHT);
            if (rightDiagonal.containsKey(position)) {
                availablePaths.put(PathType.DIAGONAL_RIGHT, rightDiagonal.get(position));
            }
        } else if (position == 10) {
            // At position 10 - can go main path (10->11) or left diagonal (10->26)
            Map<Integer, Integer> leftDiagonal = pathMaps.get(PathType.DIAGONAL_LEFT);
            if (leftDiagonal.containsKey(position)) {
                availablePaths.put(PathType.DIAGONAL_LEFT, leftDiagonal.get(position));
            }
        } else if (position == 15) {
            // At position 15 - can go main path (15->16) or right diagonal (15->25)
            Map<Integer, Integer> rightDiagonal = pathMaps.get(PathType.DIAGONAL_RIGHT);
            if (rightDiagonal.containsKey(position)) {
                availablePaths.put(PathType.DIAGONAL_RIGHT, rightDiagonal.get(position));
            }
        } else if (position == 23) {
            // Only offer path choices at 23 if we came from position 5's diagonal path
            boolean cameFromPosition5Path = previousPosition == 22; // We came through 5->21->22->23
            
            if (cameFromPosition5Path) {
                // Can choose between continuing to 28->29->20 or going to 24->25->15
                Map<Integer, Integer> leftDiagonal = pathMaps.get(PathType.DIAGONAL_LEFT);
                Map<Integer, Integer> rightDiagonal = pathMaps.get(PathType.DIAGONAL_RIGHT);
                
                // Add path to 28->29->20
                if (leftDiagonal.containsKey(position)) {
                    availablePaths.put(PathType.DIAGONAL_LEFT, leftDiagonal.get(position));
                }
                // Add path to 24->25->15
                if (rightDiagonal.containsKey(position)) {
                    availablePaths.put(PathType.DIAGONAL_RIGHT, rightDiagonal.get(position));
                }
            }
        }
        
        return availablePaths;
    }

    /**
     * Gets the next position based on the current position, roll, and chosen path type
     */
    public int getNextPosition(int currentPosition, int roll, PathType chosenPath) {
        if (roll == 0) return currentPosition;
        
        // Handle BackDo case
        if (roll == YutSet.BACKDO) {
            if (currentPosition == -1) return currentPosition;
            Integer backPos = reverseDirections.get(currentPosition);
            return backPos != null ? backPos : currentPosition;
        }

        // Handle home position
        if (currentPosition == -1) {
            return 1;
        }

        int currentPos = currentPosition;
        int stepsLeft = roll;
        PathType currentPath = chosenPath;
        
        // Set the flag when taking diagonal from 5
        if (currentPosition == 5 && chosenPath == PathType.DIAGONAL_RIGHT) {
            cameFromPosition5Diagonal = true;
        }

        System.out.println("Starting movement from position " + currentPos + 
                         " with " + stepsLeft + " steps on path " + currentPath +
                         (cameFromPosition5Diagonal ? " (coming from position 5 diagonal)" : ""));

        while (stepsLeft > 0) {
            // Get next position based on current path
            Map<Integer, Integer> pathMap = pathMaps.get(currentPath);
            Integer nextPos = pathMap.get(currentPos);
            
            // If we can't continue on current path, try switching to main path
            if (nextPos == null && currentPath != PathType.MAIN_PATH) {
                System.out.println("Cannot continue on " + currentPath + 
                                 ", switching to main path at position " + currentPos);
                currentPath = PathType.MAIN_PATH;
                pathMap = pathMaps.get(PathType.MAIN_PATH);
                nextPos = pathMap.get(currentPos);
            }

            // If still no valid next position, stay at current position
            if (nextPos == null) {
                System.out.println("No valid next position found from " + currentPos);
                return currentPos;
            }

            System.out.println("Moving from " + currentPos + " to " + nextPos + 
                             " on path " + currentPath + ", steps left: " + (stepsLeft-1));
            
            currentPos = nextPos;
            stepsLeft--;

            // If we reach position 20 and it's not the starting position
            if (currentPos == 20 && currentPosition != 19 && currentPosition != -1) {
                if (stepsLeft == 0) {
                    // If we land exactly on 20, mark as completed
                    return 30;
                } else {
                    // If we pass through 20, continue to position 1
                    currentPos = 1;
                    stepsLeft--;
                }
            }
        }

        return currentPos;
    }

    /**
     * Gets available paths from a junction position (overloaded version for backward compatibility)
     * @param position The current position
     * @return Map of path type to next position, empty if not a junction
     */
    public Map<PathType, Integer> getAvailablePaths(int position) {
        return getAvailablePaths(position, -1);
    }
}    